<html xmlns="http://www.w3.org/1999/xhtml" id="cookies">
<head>
    <title>Cookies and Sessions</title>
</head>

<body>

<h1>Cookies and Sessions</h1>
 
<warning>
Note.  Use of cookies requires the presence of LPS, the Laszlo Presentation Server.
Applications that use cookies cannot be delployed serverlessly.
</warning>
<!--========= --> 
<!-- cookies  -->
<!--========= -->
<h2>Using cookies</h2>

<p>Because HTTP is stateless, each request to the server is seen as an
independent visit by an anonymous client. Cookies are used to maintain session
state by uniquely identifying clients between requests. Servers can then use the
session to save application data, track users, deliver personalized information,
etc. Cookies are received by HTTP servers through the <tt>Cookie</tt> request
header and set using the <tt>Set-Cookie</tt> response header.</p>

<p>For security reasons, only cookies for a requested domain are sent up to the
server. A great reference for cookie information is the Unofficial Cookie FAQ at
<a href="http://www.cookiecentral.com/faq/">cookiecentral.com</a>.</p>


<h2>Proxying request and response headers</h2>

<p>The LPS proxies client request headers and backend server response
headers. Because clients always talk to the LPS, only headers in the LPS domain
will be sent. If a client makes a request to a server that has a different
domain than the LPS, incorrect headers will be forwarded.</p>

<p>For example, assume your LPS is hosted in the myhost.com domain. If your
application makes a request to a server that lives in myhost.com, then the right
headers will be sent. If the server is hosted in anotherhost.com, then the
client's myhost.com headers will be incorrectly proxied. </p>

<h2>Lifecycle of a data request</h2>

<p>As previously mentioned, all http data requests have to go through the
LPS. When the LPS receives a request, it proxies the request headers (like the
<tt>Cookie</tt> header, if found) and makes a request to the destination server
on behalf of the application. When the backend data is returned, the LPS proxies
the response headers (like the <tt>Set-Cookie</tt> header, if found) and compiles
the XML into bytecode. Once the bytecode is ready, it is sent down to the
application.</p>

<img class="illustration" src="images/cookies.png"
     title="Data request lifecycle"/>

<h2>Things to consider when deploying sessioned applications</h2>

<p>Things to keep in mind when building sessioned Laszlo apps:</p>

<ul>

    <li>Make sure your authentication server's host has the same domain as your
    LPS</li>

    <li>Because your laszlo application lives in the Flash runtime, there's no
    way to directly access your browser's cookies. A trick you can use is to
    make a data request to a JSP (or server) that responds with the cookie
    embedded in body of the response. See the next section for an example of how
    to do this.</li>

</ul>

<h2>Getting at cookies from LZX</h2>

<p>This section demonstrates how a Laszlo application can get at its cookie by
calling a JSP. The JSP will parse and display the cookie value from the
application and return the data to the application as XML. The application will
then display the information using a text datapath.</p>

<example extract="false">
&lt;canvas width="500" height="200"&gt;

    &lt;dataset name="request" type="http" src="cookie.jsp" autorequest="true" /&gt;

    &lt;view x="20" y="20" layout="spacing: 5" &gt;
        &lt;text&gt;&lt;b&gt;cookie: &lt;/b&gt;&lt;/text&gt;
        &lt;text resize="true" selectable="true" multiline="true" width="450" 
              datapath="request:/cookie[1]/text()" /&gt;
    &lt;/view&gt;

&lt;/canvas&gt;
</example>

<p>The LZX expects the format of the returned XML to look like:</p>

<pre>
&lt;cookie&gt;COOKIE&lt;/cookie&gt;
</pre>

<p>Copy the LZX code and save it in a file called <tt>cookie.lzx</tt>. Make sure you
can access this file using your LPS. Before you run it, you'll need to create
the JSP.</p>

<p>Take the following JSP code and drop it in a web application directory
that you can access by URL. Make sure the LZX code's dataset is pointing to this
URL, i.e., replace the <code>src</code> value with your URL.</p>

<example extract="false" title="jsp code for generating cookie">
&lt;%@ page import="java.util.*" %&gt;

&lt;%
    response.setHeader("Content-Type","text/xml");
%&gt;

&lt;cookie&gt;
&lt;%
    Enumeration headers = request.getHeaderNames();
    if (headers != null) {
        while (headers.hasMoreElements()) {
            String h = (String)headers.nextElement();
            if (h.equalsIgnoreCase("Cookie")) {
                out.println(request.getHeader(h));
                break;
            }
        }
    } 
%&gt;
&lt;/cookie&gt;
</example>
<h2>Using cookies to recover state</h2>
<p>
The following example shows an LZX program that uses a java sever page (JSP) to set and store a cookie. (The jsp program is shown below.)

<fixme>
Explain how it works
</fixme>
</p>

<example title="combobox that creates cookie" >
&lt;canvas height="100" &gt;

    &lt;!-- dataset to set cookie with --&gt;
    &lt;dataset name="dsSetCookie" type="http" <em>src="../cookie.jsp" </em>/&gt;

    &lt;!-- Set previous set item during start --&gt;
    &lt;dataset name="dsGetCookie" type="http" <em>src="../cookie.jsp</em>" 
             querystring="name=mycookie&amp;amp;op=get" autorequest="true" /&gt;
    &lt;datapointer name="dpCookie" xpath="dsGetCookie:/info[1]/data[1]/item[1]/text()"&gt;
        &lt;method event="ondata" args="d"&gt;
            if (d != '') cbox1.selectItem(d);
        &lt;/method&gt;        
    &lt;/datapointer&gt;


    &lt;!-- combobox items --&gt;
    &lt;dataset name="items"&gt;
        &lt;item value="item1" &gt;item one&lt;/item&gt;
        &lt;item value="item2" &gt;item two&lt;/item&gt;
        &lt;item value="item3" &gt;item three&lt;/item&gt;
        &lt;item value="item4" &gt;item four&lt;/item&gt;
    &lt;/dataset&gt;

    &lt;combobox id="cbox1" x="20" y="20" width="130" defaulttext="choose one..."&gt;
        &lt;textlistitem datapath="items:/item/" text="$path{'text()'}"
                      value="$path{'@value'}"&gt;
            &lt;!-- when item is selected, save choice in cookie by calling JSP --&gt;
            &lt;method event="onclick"&gt;
            &lt;![CDATA[
                dsSetCookie.setQueryString("name=mycookie&amp;op=set&amp;item=" + this.value);
                dsSetCookie.doRequest();
            ]]&gt;
            &lt;/method&gt;
        &lt;/textlistitem&gt;
    &lt;/combobox&gt;
&lt;/canvas&gt;
</example>
<example extract="false" title="cookie.jsp">


&lt;%@ page import="java.util.*,java.io.*" contentType="text/xml" %&gt;
&lt;info&gt;
&lt;%!
/** 
 * Get cookie to store information.
 * @param name: name of cookie
 * @param cookies: client cookies
 */
Cookie getCookie(String name, Cookie[] cookies) {

    if (cookies != null) {
        for (int i=0; i &lt; cookies.length; i++) { 
            Cookie cookie = cookies[i];
            if (cookie.getName().equals(name)) {
                return cookie;
            }
        }
    }

    return new Cookie(name, "");
}

void showCookie(JspWriter out, Cookie cookie) throws IOException {
    out.println("&lt;success/&gt;");
    out.print("&lt;data&gt;");
    Map map = getCookieValues(cookie);    
    Iterator iter = map.entrySet().iterator();
    while (iter.hasNext()) {
        Map.Entry e = (Map.Entry)iter.next();
        String k = (String)e.getKey();
        out.println("&lt;" + k + "&gt;" + (String)e.getValue() + "&lt;/" + k + "&gt;\n");
    }
    out.println("&lt;/data&gt;");    
}

Map getCookieValues(Cookie cookie) throws IOException{
    // saved as key1=val1&amp;key2=val2&amp;...
    String str = cookie.getValue();
    StringTokenizer st = new StringTokenizer(cookie.getValue(), "&amp;");
    Map map = new HashMap();
    while (st.hasMoreTokens()) {
        String pair = st.nextToken();
        int index = pair.indexOf('=');
        if (index == -1 || index == 0) continue;
        String k = pair.substring(0, index);
        String v = pair.substring(index+1);
        map.put(k, v);
    }
    return map;
}

/**
 * Copy parameter key/value pairs into cookie. Skips "op" and "name" parameters.
 */
void setValues(JspWriter out, Cookie cookie, Map params) throws IOException{

    Iterator iter = params.entrySet().iterator();
    Map cookieValues = getCookieValues(cookie);
    while (iter.hasNext()) {
        Map.Entry e = (Map.Entry)iter.next();
        String k = (String)e.getKey();

        // skip op and name
        if (k.equals("op") || k.equals("name")) continue;

        String[] values = (String[])e.getValue();
        cookieValues.put(k, values[0]);
    }

    String newvalue = "";
    iter = cookieValues.entrySet().iterator();
    while (iter.hasNext()) {
        Map.Entry e = (Map.Entry)iter.next();
        String k = (String)e.getKey();
        String v = (String)e.getValue();
        newvalue += k + "=" + v + "&amp;";
    }

    cookie.setValue(newvalue);
}

%&gt;

&lt;%
    // operation
    String op = request.getParameter("op");
    if ( op == null || op.equals("") ) {
        out.println("&lt;status&gt;error&lt;/status&gt;\n&lt;message&gt;op: null or empty&lt;/message&gt;\n");
        out.println("&lt;/info&gt;");
        return;
    }

    // name of cookie
    String name = request.getParameter("name");
    if (name == null || name.equals("") ) {
        out.println("&lt;status&gt;error&lt;/status&gt;\n&lt;message&gt;name: null or empty&lt;/message&gt;\n");
        out.println("&lt;/info&gt;");
        return;
    }

    Cookie cookie = getCookie(name, request.getCookies());
    if ( op.equals("get") ) {
        showCookie(out, cookie);
    } else if ( op.equals("set") ) {
        setValues(out, cookie, request.getParameterMap());
        response.addCookie(cookie);
        showCookie(out, cookie);
    } else {
        out.println("&lt;status&gt;error&lt;/status&gt;\n&lt;message&gt;no such op: " + op + "&lt;/message&gt;\n");
    }
%&gt;
&lt;/info&gt;

</example>


<fixme>
Is there going to be a place in the dguide that talks about 
how to set 
up (say) a combobox to recover its state from a previous 
application 
session with a cookie? If you were to categorize this, it would be 
part of a larger set of issues, such as "Common application 
development problems". A bunch of the Tips would fall into this 
</fixme>
<fixme>
You can't get at the cookie from the response headers. 
The only cookie header you can access is the "set-cookie" header. 
At the moment, you'll have to write a JSP to get at your cookie. 
Ideally, it'd be nice if there was a getCookie() call in the LFC.
</fixme>
</body>
</html>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2001-2004 Laszlo Systems, Inc.  All Rights Reserved.              *
* Use is subject to license terms.                                            *
* X_LZ_COPYRIGHT_END ****************************************************** -->
